import {
  buildinTaskFun,
  funcCall,
  globparam,
  jtaskItem,
  OperMode,
  taskItem,
  taskstep,
  taskTrack,
} from "../utils/taskGraphInterface";
import { taskStorage } from "./taskStorage";
import { common } from "@kit.AbilityKit";
import { AccessibilityExtensionContext } from "@kit.AccessibilityKit";
import { ImgObj } from "../utils/recallInterface";
import { caps2str } from "../VecDbModel/search";
import { promptAction } from "@kit.ArkUI";

export class taskPrompt {
  NavigatorPrompt(task: string, tracks: taskTrack[], imgObj0: ImgObj): string {
    let trackstr = this.tracks4LLM(tracks, true)
    return "你是一个手机自动化操作领航员。你要确保这个任务的正确执行:" + task + ".这是之前手机操作的步骤历史" +
      trackstr +
      ".现在第一个任务，分析这个步骤历史，判断是否任务在顺利进行或停止不前？再观察这张图片，是否和任务有偏离？" +
      "第二个任务，你要选择下一个任务时使用哪种模式操作手机？" +
      "你需要回答一个JSON。格式为{nextOperMode:string(\"DOM\"|\"VLMtag\"|\"LLMtag\"|\"back\"|\"completed\")" +
      "(=>VLMtag(ocr recognize all texts and send to VLM), here are the OCR-tags [" +
    caps2str(imgObj0.caps, ",") + "]" +
      "VLMtag agent can only see these tags and click/scroll/type these position bounded tags.Judge if it is capable for task completion,enter VLMtag." +
      "=>LLMtag(ocr recognize all texts and send to LLM),identical with VLMtag,difference is it cannot read image but is faster." +
      "=>DOM(system accessibility DOM tree).Used when VLMtag struggles to get textual and visual clues on the page,if VLMtag/LLMtag operation history is twisty.Or if scroll or textInput is needed)" +
      "=>back is pressing back button,If the page deviates from the task" +
      "=>completed is called when you believe the task is completed" +
      "=>btw,If the next page is likely a browser webpage,then system accessibility DOM tree is unavailable,so DON'T CHOOSE DOM in that case)" +
      "}" + "你只能返回一个JSON！！！,里面只用你填一个字段：nextOperMOde。不要在JSON之外有多余解释性文字！！！。"
  }

  createWorkflow(dispspeech: string): string {
    return "用户给出的任务是：" + dispspeech +
      ".你是一个工作流生成助手。现在有这些类型的节点可供你构成一个！！！有向无环！！！图：" +
      "回复格式为一个JSON块。不要加解释性文字。" +
      "[{ id: number(assign an UNIQUE autoincrement id for each node.Start from 0)," +
      "  name: string(to describe the functionality of the node.Short and concise)," +
      "  inputType: \"text\"|\"image\"|\"text_image\"," +
      "  outputType: \"text\"|\"image\"|\"text_image\"(注意，输入类型会限制输出类型的取值,以下是特定输入取值对应的输出可取的值:" +
      "=>1.(text=>text):LLM,Receives text,outputs text." +
      "=>2.(text=>image):Diffusion Model.Receives CONCISE AND LIMITED LENGTH text,outputs image." +
      "=>3.(text=>text_image):WebSearch API.inputs query text(normal web search,so the text should be general and concise),outputs IMAGE AND TEXT.)." +
      "=>4.(text_image=>text):Visual LLM.inputs IMAGE AND TEXT,outputstext.)," +
      "  prompt:string(as long as the model API called inside the node accepts text as one of its accepted input types)," +
      "the prompt text in this field will be concatenated with the node's received input.)," +
      "  prev:number[](前驱节点id列表)," +
      "  next:number[](后继节点id列表)},{id:1,...}]"
  }

  VLMdevPrompt(task: string): string {
    return "你是一个手机操作观察员。请判断：当前的手机页面图片 是否是完成这个任务所必要的UI路径，还是页面和这个任务没什么关系。任务是<" +
      task +
      ">" + ".注意，现在你有了答案后返回{pathCorrect:boolean}。你只能回复一个JSON代码块。不要有多余的解释性文字！！！"
  }

  ts1: taskStorage

  constructor(context: common.UIAbilityContext | AccessibilityExtensionContext) {
    this.ts1 = new taskStorage(context)
  }

  async task2fun(): Promise<string> {
    let alltasks: taskItem[] = await this.ts1.getAllTasks()
    alltasks = alltasks.filter((task) => {
      return !buildinTaskFun.includes(task.name)
    })
    let nonBuildInFunstr = ""
    if (alltasks.length != 0) {
      let tasksstr: string[] = alltasks.map((taskitem: taskItem) => {
        return JSON.stringify(taskitem, ["name", "gparams", "task"]).replace(/\\"/g, "")
      })
      nonBuildInFunstr = "现在有下面这些已有函数。" + JSON.stringify(tasksstr) +
        "第一种情况，如果已有函数和任务匹配，你可以选择一个已有函数来调用，并填gparams中说明的函数形参." +
        "(！！！千万注意，如果前面发给你的这些JSON格式的函数声明的参数对象中value中带有\"=>\"" +
        "说明这个参数有默认值，你调用的时候尽量不要在stepparams中加上这个参数对象，除非任务必要。).第二种情况：如果没有函数可以被调用，那么"
    }
    return "上面这是你的任务，" + nonBuildInFunstr +
      "需要你创造一个新函数," +
      "并且设计一定程度上的泛化(创建一些形参)来应对你的任务的末端的具体内容的变化。你只能返回JSON。返回格式" +
      "{name:functionName," +
      "gparams:[{key:string,value:string},{...}]," +
      "task:如果你在创造一个新的函数，那在这里填入你泛化过后的任务描述，否则填\"\", " +
      "steps:[](leave an empty list here.Don\"t forget)," +
      "historycalls:[](leave an empty list here.Don\"t forget)}" +
      "!!!注意，不管是用已有函数或创建性函数，你必须在gparams[i].value中写具体调用的值.而具体值中单词间的空格用连词符-代替。" +
      "你只能回答单一的JSON对象！！！。不要有任何解释性文字！！！"
  }


  async writeSteps(excludeName: string, tracks: taskTrack[]) {
    let TAG = "_:_writeSteps"
    let alltasks: taskItem[] = await this.ts1.getAllTasks()
    alltasks = alltasks.filter((task: taskItem) => {
      return task.name != excludeName
    })
    let thistask: taskItem | undefined = alltasks.find((task: taskItem) => {
      return task.name == excludeName
    })
    if (thistask == undefined) {
      console.error(TAG, "thisTask named", excludeName, "is undefined <taskItem>")
    }
    let filteredtasks: string[] = alltasks.map((taskitem: taskItem) => {
      return JSON.stringify(taskitem, ["name", "gparams", "task"]).replace(/\\"/g, "")
    })
    let thisfuncName = (thistask != undefined) ? (thistask?.name) : ""
    let midstr = ""
    if (tracks.length > 0) {
      midstr = "这是这个函数内部调用的子函数序列：" + this.tracks4LLM(tracks) + "。但是这些子函数序列的实参并未和主函数" +
        thisfuncName + "的对外形参匹配。并且有些子函数是操作失误引起的，比如重复调用输入同一个内容的函数，你应该去除它们。请按以下要求回复："
    } else {
      midstr = "。现在有这么些函数可以供你调用：" +
      JSON.stringify(filteredtasks).replace(/\\"/g, "")
    }
    let sumstr =
      "作为一个手机操作助手，这是你要写的" + thisfuncName + "函数的声明：" +
      JSON.stringify(thistask, ["name", "gparams", "task"]) +
        midstr +
        "请千万只用输出一个JSON列表，不输出多余文字。格式如下：" +
        "[{functionName:string,stepparams:[{key:text,value:string,track:\"\"(just leave it empty),globalkey:string" +
        "(IMPORTANT:if you believe one of the declared params of" + thisfuncName +
        " should be passed to this param object,then fill the globalkey field with the desired " + thisfuncName +
        " param },{...}]},{...}]" +
        "你只能返回一个JSON！！！不要多余解释性文字！！！。"
    // "请输出一个JSON列表，是一个全局函数形参表和一个步骤列表。格式如下：{globparams:[{key:string,value:string},{...}]," +
    // "steps:[{functionName:string,stepparams:[{key:text,value:string,globalkey:string},{...}]},{...}]}."
    // "(如果步骤列表steps的函数形参stepparams[i]应该被改变以迎接同一个任务的不同的具体内容，比如打开网页搜索中的搜索内容，那么stepparams[i].globalkey填入和gloparams[i].key对应的值。程序会读取对应gloparams[i].key的value值。" +
    // "如果对于这个任务的不同应用场景，此项stepparams[i]都应该是个常值，比如打开网页搜索中的打开应用名字固定就是browser，请globalkey填const，程序会读取globalkey旁边的value的字段值.这时不用在gloparams[i]中写这一项，因为它不再提供全局参数入口)"
    // "请注意这个steps列表中每个函数被调用的形参中，" +
    // "大部分应该填value是空字符串，而是填globalkey,对应globparam[i].key的字段内容，" +
    // "而步骤函数的形参值stepparams[i].value将从对应globparam[i].value中获取。" +
    // "这种形参的映射关系相当于将函数列表封装成一个基于任务的主函数供复用。" +
    // "如果对于这个任务来说，有些steps中的函数形参应该是字符串常量，" +
    // "即对于这个任务内容给定的情况下，这些形参会改变任务本身而非同一个任务的不同应用场景，" +
    // "那么在stepparams[i].globkey字段填const，然后在stepparams[i].value中填具体值.你的回答只能包含JSON，不要多余的解释性文字"
    return sumstr
  }

  tagPrompt(taskStr: string, type: OperMode, ocrPageDesc: string) {
    promptAction.showToast({ message: "Mode:" + OperMode[type] + " " + ocrPageDesc })
    let tracks = taskStorage.tracks
    let historystr: string = ""
    if (tracks.length != 0) {
      historystr =
        "This is the records of what your predecessors have done to accomplish this task" +
        this.tracks4LLM(taskStorage.tracks)
    }
    let pageDesc: string = ""
    let actionstr: string = ""
    if (type == OperMode.LLMtag) {
      pageDesc = ocrPageDesc + "Above is all the text-TAGs on the page,each with an Id.Some of the texts are buttons."
      actionstr = "action:string(\"click\"|\"scroll\"|\"type\",performing action on a certain text-TAG given above),"

    } else if (type == OperMode.VLMtag) {
      pageDesc = ocrPageDesc + "Above is all the text-TAGs on the page,each with an Id.Some of the texts are buttons."
      actionstr =
        "action:string(\"click\"|\"scroll\"|\"type\" You are to perform action on a certain text-TAG given above),"
    }
    // pageDesc = ocrPageDesc + "Above is all the text-TAGs on the page,each with an Id.Some of the texts are buttons."
    return pageDesc + "You are a robot process automation controller.Your big task is" + taskStr +
      historystr +
      "Now generate a JSON as required to move forward a step towards the big task." +
      "{pageDesc:string(use Chinese.please describe the main functionalities of this GUI.please limit its length to 10 words)," +
      actionstr +
      "targetText:string(the target text-TAG's text)," +
      "targetId:number(the target text-TAG's id)" +
      "If action=click,enter the targetId of the text you want to click." +
      "If action=scroll,enter the targetId that helps locates where you want to start the scroll action." +
      "If action=type,enter the targetId of the hint text of a certain TextInput component.)," +
      "inputText:string(if action=type,else empty)," +
      "scrollDir:\"vf\"|\"vb\"|\"hf\"|\"hb\"" +
      "(h is scrolling horizontally,v is vertically,f is scrolling forward,b backward)," +
      "keyCode:number(Here defines some keyCode that you can call if needed.Else, fill in -1." +
      "VOLUME_UP=16 VOLUMN_DOWN=17 SHOOT_CAMERA=19)," +
      " taskStatus:string(based on the page descriptions and the task:" +
    this.AlreadyCompleted + this.NotCompleted + this.NoClue + ")}" +
      "YOU MUST ONLY REPLY ONE SINGLE JSON BLOCK, NO ANY EXPLANATORY TEXT.ONLY ONE JSON BLOCK!!EXTREMELY SHORT AND CONCISE REPLY"
    //"现在判断为了完成任务，应该做这三件事情中的哪一件？1.点击序号为targetId文字 2.从某个文字的位置处开始滑动 3.。每串文字旁边有黄色方块写着数字，要点哪个文字就选哪个数字。你的回答只能包含JSON！！格式如下" +
    // "The (xcoordinate + ycoordinate) of touchPoint will increase,b:backward.The x+y decreases.We often use vf.Leave empty if you are not scrolling)." +
  }

  AlreadyCompleted =
    "=>Based on your previous replies and common sense about modern phone system, If the task is already fulfilled without further action, fill in \"AlreadyCompleted\".\n"
  NotCompleted =
    "=>If the task needs further action to be fulfilled, fill in \"NotCompleted\".\n"
  NoClue =
    "=>After you have scrutinized this task and thought of the implied tasks intailed by this task, If you believe even traversing further from this page will still not lead to the completion of this task, fill in \"NoClue\".And fill targetId with-1).\n"

  tracks4LLM(tracks: taskTrack[], addOperMode: boolean = false) {
    let TAG = "_:_tracks4LLM"
    // let rtnstr = JSON.stringify(tracks, ["call", "functionName", "gparams","key","value",
    //   "LMpageDesc", "operMode"])
    let funcList: string[] = tracks.map((track) => {
      return track.LMpageDesc + ":" + ((addOperMode) ? ("<" + track.operMode + ">") : "") +
      this.funcCall4LLM(track.call)
    })
    let rtnstr = JSON.stringify(funcList).replace(/"/g, "").replace(/\\"/g, "")
    console.error(TAG, "providing", rtnstr.replace(/\n/g, ""))
    return rtnstr
  }

  funcCall4LLM(call: funcCall) {
    return call.functionName + "(" + call.gparams.reduce((acc, param, index) => {
      if (index > 0) {
        acc += ",";
      }
      acc += param.key + ":" + param.value;
      return acc;
    }, "") + ")"
  }

  DOMprompt(taskStr: string, pageDescH: string, extraInfo: string): string {
    let TAG = "_:_DOMprompt"
    let historystr: string = ""
    let tracksStr: string = ""
    let tracks = taskStorage.tracks
    if (tracks.length != 0) {
      tracksStr = this.tracks4LLM(tracks)
      historystr =
        "This is the records of what your predecessors have done to accomplish this task" + tracksStr
    }
    console.error(TAG, "taskStr", taskStr.length, taskStr.replace(/\n/g, "").replace(/\s+/g, " ").length,
      taskStr.replace(/\n/g, "").replace(/\s+/g, " "))
    console.error(TAG, "pageDescH", pageDescH.length, pageDescH.replace(/\n/g, "").replace(/\s+/g, " ").length,
      pageDescH.replace(/\n/g, "").replace(/\s+/g, " "))
    console.error(TAG, "extraInfo", extraInfo.length, extraInfo.replace(/\n/g, "").replace(/\s+/g, " ").length,
      extraInfo.replace(/\n/g, "").replace(/\s+/g, " "))
    console.error(TAG, "historystr track length", tracksStr.length,
      tracksStr.replace(/\n/g, "").replace(/\s+/g, " ").length,
      tracksStr.replace(/\n/g, "").replace(/\s+/g, " "))

    taskStr = taskStr.replace(/\n/g, "").replace(/\s+/g, " ")
    pageDescH = pageDescH.replace(/\n/g, "").replace(/\s+/g, "")
    extraInfo = extraInfo.replace(/\n/g, "").replace(/\s+/g, " ")
    historystr = historystr.replace(/\n/g, "").replace(/\s+/g, " ")

    let sumstr = " 你的任务是： " + taskStr + "。现在有个手机页面GUI的JSON：" + pageDescH + extraInfo + historystr +
      "。请选择一个CLICKABLE或SCROLLABLE组件的id，以如下格式返回结果：" +
      "{taskStatus:" +
      "string(based on the page descriptions and the task:" +
    this.AlreadyCompleted + this.NotCompleted + this.NoClue +
      "pageDesc:string(use Chinese.please describe the main functionalities of this GUI.please limit its length to 10 words)," +
      "action:string(\"click\"|\"scroll\"|\"type\"|\"back\"|\"keyCode\")," +
      "targetText:string(text of the target element from the GUI JSON.If JSON tells you it has no text fill in \"\")," +
      "targetId:number(id from the GUI JSON.Note that ONLY CLICKABLE OR SCROLLABLE OR TEXTINPUT ELEMENT CAN BE THE TARGET)," +
      // "action:string(click;scrollForward(to increase the value of a certain element or scroll the page further forward);scrollBackward)," +
      // "text:string(if the component you selected is a TextArea or TextInput, fill in text here, else, fill in N/A}," +
      "If action=click,enter the targetId of the text you want to click." +
      "If action=scroll,enter the targetId that helps locates where you want to start the scroll action." +
      "If action=type,enter the targetId of the hint text of a certain TextInput component.)," +
      "inputText:string(if action=type,else empty)," +
      "scrollDir:\"vf\"|\"vb\"|\"hf\"|\"hb\"" +
      "(h horizontal,v vertical,f is scrolling forward or to increase value of a certain element,b backward.)," +
      "keyCode:number(Here defines some keyCode that you can call if needed.Else, fill in -1." +
      "VOLUME_UP=16 VOLUMN_DOWN=17 SHOOT_CAMERA=19)," +
      // "willTurn:bool(if you can be 90 percent sure that operation on the target element can not lead to page to turn, fill in false.If you are not sure, fill in true)" +
      "你的回答只能只能输出字符串的标准JSON格式，不能有其他格式回答"
    console.error(TAG, "SUMSTR", sumstr.length, sumstr.replace(/\n/g, ""))
    return sumstr
  }
}




